﻿@using AntDesign.Internal
@inherits AntDesignTestBase	
@code {

	[Fact]
	public void Input_Bordered_sets_class()
	{
		//Arrange
		var cut = Render<AntDesign.Input<string>>(@<AntDesign.Input TValue="string" Bordered="false"/>);

		//Act
		ITokenList classList = cut.Find("input").ClassList;		
		
        //Assert
		classList.Contains("ant-input-borderless").Should().BeTrue();
	}

	[Fact]
	public async Task Input_FocusClear_behavior_should_clear()
	{
		//Arrange
#pragma warning disable CS0618 // Type or member is obsolete
        JSInterop.SetupVoid(JSInteropConstants.Focus, _ => true).SetVoidResult();
#pragma warning restore CS0618 // Type or member is obsolete
		AntDesign.Input<string>? input = null;
		string value = "initial value";
		var cut = Render<AntDesign.Input<string>>(
		@<AntDesign.Input 
			@ref="@input" 
			@bind-Value=value 
			DebounceMiliseconds="0"/>
	);
		//Act
		await cut.InvokeAsync(() => input!.Focus(FocusBehavior.FocusAndClear));
		//Assert           		
		value.Should().BeNull();
#if NET6_0_OR_GREATER
		JSInterop.VerifyFocusAsyncInvoke();
#endif
	}

	[Fact]
	public async Task Input_FocusBehavior_passed_to_js()
	{
		//Arrange
#pragma warning disable CS0618 // Type or member is obsolete
        var plannedInvocation = JSInterop.SetupVoid(JSInteropConstants.Focus, _ => true).SetVoidResult();
#pragma warning restore CS0618 // Type or member is obsolete		
		AntDesign.Input<string>? input = null;
		string value = "initial value";
		var cut = Render<AntDesign.Input<string>>(
		@<AntDesign.Input 
			@ref="@input" 
			@bind-Value=value 
			DebounceMiliseconds="0"/>
	);
		//Act
		await input!.Focus(FocusBehavior.FocusAndSelectAll);
		plannedInvocation.SetVoidResult();
		//Assert           		
		plannedInvocation.Invocations.First().Arguments[2].Should().Be(FocusBehavior.FocusAndSelectAll);	
	}

	[Fact]
	public void Input_Blur_passed_to_js()
	{
		//Arrange
		var plannedInvocation = JSInterop.SetupVoid(JSInteropConstants.Blur, _ => true);
		AntDesign.Input<string>? input = default;
		string value = "initial value";
		var cut = Render<AntDesign.Input<string>>(
		@<AntDesign.Input 
			@ref="@input" 
			@bind-Value=value 
			DebounceMiliseconds="0"/>
	);
		//Act
		input?.Blur();
		plannedInvocation.SetVoidResult();
		//Assert           		
		plannedInvocation.Invocations.Should().HaveCount(1);
	}

	[Fact]
	public void Input_InputElementSuffixClass_is_added_to_class()
	{
		//Arrange						
		var cut = Render<AntDesign.Input<string>>(
			@<AntDesign.Input TValue="string" InputElementSuffixClass="testClass"/>
	);						
		//Act
		ITokenList classList = cut.Find("input").ClassList;
		//Assert           
		classList.Contains("testClass").Should().BeTrue();
	}

    [Fact]
    public void Input_no_warpper_apply_class()
    {
        string? value = null;
        string className = "test-class";
        var cut = Render<AntDesign.Input<string>>(@<AntDesign.Input @bind-Value="@value" Class="@className" />);

        cut.Find("input").ClassList.Contains(className).Should().BeTrue();
    }

	[Fact]
	public void Input_show_clearButton_when_input_set()
	{
		//Arrange
		var cut = Render<AntDesign.Input<string>>(
			@<AntDesign.Input TValue="string" AllowClear/>);
        //Act
        bool isClearButtonHidden = cut.Find("span.ant-input-clear-icon").GetAttribute("class")!.Contains("ant-input-clear-icon-hidden");
        cut.Find("input").Input("test");		
		//Assert  
		isClearButtonHidden.Should().BeTrue();
        cut.Find("span.ant-input-clear-icon").GetAttribute("class")!.Contains("ant-input-clear-icon-hidden").Should().BeFalse();
    }

    [Fact]
	public void Input_show_clearButton_with_suffix()
	{
		//Arrange
		var cut = Render<AntDesign.Input<string>>(
			@<AntDesign.Input TValue="string" AllowClear ><Suffix>suffix</Suffix></AntDesign.Input>);
        //Assert
         cut.Find("span.ant-input-clear-icon").GetAttribute("class")!.Contains("ant-input-clear-icon-has-suffix").Should().BeTrue();
    }

	[Fact]
	public void Input_hide_clearButton_when_input_reset()
	{
		//Arrange		
		AntDesign.Input<string>? input = null;		

		var cut = Render<AntDesign.Input<string>>(
			@<AntDesign.Input @ref="@input" Value="@("test")" AllowClear/>);
		//Act
		bool isClearButtonHidden = cut.Find("span.ant-input-clear-icon").GetAttribute("class")!.Contains("ant-input-clear-icon-hidden");
		cut.Find("input").Input("");		
		//Assert  
		isClearButtonHidden.Should().BeFalse();
        cut.Find("span.ant-input-clear-icon").GetAttribute("class")!.Contains("ant-input-clear-icon-hidden").Should().BeTrue();
	}

    [Fact]
    public void Input_show_clearButton_when_show_clear_true()
    {
        //Arrange
        var cut = Render<AntDesign.Input<string>>(
            @<AntDesign.Input TValue="string" AllowClear ShowClear="true"/>);
        // Not hidden even when empty
        cut.Find("span.ant-input-clear-icon").GetAttribute("class")!.Contains("ant-input-clear-icon-hidden").Should().BeFalse();

        cut.Find("input").Input("test");
        // Not hidden after adding input
        cut.Find("span.ant-input-clear-icon").GetAttribute("class")!.Contains("ant-input-clear-icon-hidden").Should().BeFalse();
    }

    [Fact]
    public void Input_hide_clearButton_when_show_clear_false()
    {
        //Arrange
        var cut = Render<AntDesign.Input<string>>(
            @<AntDesign.Input TValue="string" AllowClear ShowClear="false" />);
        // Hidden when empty
        cut.Find("span.ant-input-clear-icon").GetAttribute("class")!.Contains("ant-input-clear-icon-hidden").Should().BeTrue();

        cut.Find("input").Input("test");
        // Hidden even after adding input
        cut.Find("span.ant-input-clear-icon").GetAttribute("class")!.Contains("ant-input-clear-icon-hidden").Should().BeTrue();
    }

    [Fact]
    public void Input_default_change()
    {
        //Arrange
        string? value = null;
        var cut = Render<AntDesign.Input<string>>(@<AntDesign.Input @bind-Value=@value />);
        //Act
        cut.Find("input").Input("newValue"); // get value on input
        cut.Find("input").Change("newValue"); // binding value on change
        //Assert
        cut.Instance.Value.Should().Be("newValue");
    }

	[Fact]
	public async Task Input_Debounce_sets_value()
	{
		//Arrange	
		string? value = null;
		int debounce = 50;
		var cut = Render<AntDesign.Input<string>>(@<AntDesign.Input @bind-Value=@value DebounceMilliseconds="@debounce"/>);
		//Act
		cut.Find("input").Input("newValue");
		cut.Find("input").KeyUp(String.Empty);
		//Assert		
		await Task.Delay(debounce+100);		

		cut.Instance.Value.Should().Be("newValue");
	} 

    [Fact]
    public void Input_Debounce_sets_zero()
    {
        //Arrange
        string? value = null;
        int debounce = 0;
        var cut = Render<AntDesign.Input<string>>(@<AntDesign.Input @bind-Value=@value DebounceMilliseconds="@debounce" />);
        //Act
        cut.Find("input").Input("newValue");
        cut.Find("input").KeyUp(String.Empty);
        //Assert

        cut.Instance.Value.Should().Be("newValue");
    }

    [Fact]
    public async Task Input_with_bindOnInput()
    {
        //Arrange
        string? value = null;

        var cut = Render<AntDesign.Input<string>>(@<AntDesign.Input @bind-Value=@value BindOnInput />);
        //Act
        cut.Find("input").Input("newValue");
        cut.Find("input").KeyUp(String.Empty);
        //Assert
        await Task.Delay(300);

        value.Should().Be("newValue");
        cut.Instance.Value.Should().Be("newValue");
    }

    [Fact]
    public void Input_MaxLength_Adds_Attribute_To_Input()
    {
        string? value = null;
        var cut = Render<AntDesign.Input<string>>(@<AntDesign.Input @bind-Value=@value MaxLength="50" />);

        var textAreaElement = cut.Find("input");

        textAreaElement.Attributes.Single(x => x.Name.ToLowerInvariant() == "maxlength").Value.Should().Be("50");
    }

    [Fact]
    public void Input_MaxLength_Adds_Attribute_To_Input_After_Initial_Render()
    {
        string? value = null;
        var cut = Render<AntDesign.Input<string>>(@<AntDesign.Input @bind-Value=@value />);

        cut.Find("input").Attributes.SingleOrDefault(x => x.Name.ToLowerInvariant() == "maxlength").Should().BeNull();

        cut.SetParametersAndRender(parameters => parameters.Add(x => x.MaxLength, 50));

        cut.WaitForAssertion(() => cut.Find("input").Attributes.Single(x => x.Name.ToLowerInvariant() == "maxlength").Value.Should().Be("50"));
    }

    class TestModel
    {
       public int value { get; set; }
    }

    #if NET6_0_OR_GREATER
    [Fact]
    public void Form_convert_error_status()
    {
        JSInterop.Setup<AntDesign.JsInterop.Window>(JSInteropConstants.GetWindow)
			.SetResult(new AntDesign.JsInterop.Window());
        TestModel model  = new();
        Input<int> input=null; 
        var cut = Render<AntDesign.Form<TestModel>>(
        @<Form Model="model" ValidateOnChange>
            <FormItem>
                <Input @ref="input" @bind-Value=@context.value DebounceMilliseconds="0" />
            </FormItem>
        </Form>
    );

        cut.Find("input").Input("newValue");
        cut.Find("input").KeyUp(String.Empty);	

        var isValid = cut.Instance.Validate();

        isValid.Should().Be(false);
        input?.ValidationMessages.Should().Contain("value field isn't valid.");
    }
    #endif

    [Fact]
    public void Input_WrapperClass_Test()
    {
        //Arrange
        var cut = Render<AntDesign.Input<string>>(@<AntDesign.Input TValue="string" WrapperClass="test-wrapper-class"><AddOnAfter>AddOnAfter</AddOnAfter></AntDesign.Input>);

        //Act
        ITokenList classList = cut.Find("span.ant-input-group-wrapper").ClassList;

        //Assert
        classList.Contains("test-wrapper-class").Should().BeTrue();
    }

    [Fact]
    public void Input_DefaultToEmptyString_True_Returns_EmptyString()
    {
        //Arrange
        string? value = "test";
        var cut = Render<AntDesign.Input<string>>(@<AntDesign.Input @bind-Value="@value" DefaultToEmptyString="true" />);

        //Act
        cut.Find("input").Input(null);
        cut.Find("input").Change(null);

        //Assert
        cut.Instance.Value.Should().Be(string.Empty);
        value.Should().Be(string.Empty);
    }

    [Fact]
    public void Input_DefaultToEmptyString_False_Returns_Null()
    {
        //Arrange
        string? value = "test";
        var cut = Render<AntDesign.Input<string>>(@<AntDesign.Input @bind-Value="@value" DefaultToEmptyString="false" />);

        //Act
        cut.Find("input").Input(null);
        cut.Find("input").Change(null);

        //Assert
        cut.Instance.Value.Should().BeNull();
        value.Should().BeNull();
    }

    public async Task Input_OnPressEnter_Should_Trigger_Callback()
    {
        //Arrange
        var onPressEnterInvoked = false;
        Func<PressEnterEventArgs, Task> action = args => { onPressEnterInvoked = true; return Task.CompletedTask; };
        var cut = Render<AntDesign.Input<string>>(@<AntDesign.Input TValue="string" OnPressEnter="@(action)" />);
        
        //Act
        var input = cut.Find("input");
        await input.KeyPressAsync(new KeyboardEventArgs { Code = "Enter" });
        
        //Assert
        onPressEnterInvoked.Should().BeTrue();
    }

    [Fact]
    public async Task Input_OnPressEnter_Should_Not_Trigger_On_Other_Keys()
    {
        //Arrange
        var onPressEnterInvoked = false;
        Func<PressEnterEventArgs, Task> action = args => { onPressEnterInvoked = true; return Task.CompletedTask; };
        var cut = Render<AntDesign.Input<string>>(@<AntDesign.Input TValue="string" OnPressEnter="@(action)" />);
        
        //Act
        var input = cut.Find("input");
        await input.KeyPressAsync(new KeyboardEventArgs { Code = "KeyA" });
        
        //Assert
        onPressEnterInvoked.Should().BeFalse();
    }

    [Fact]
    public async Task Input_OnPressEnter_Should_Provide_KeyboardEventArgs()
    {
        //Arrange
        KeyboardEventArgs? capturedArgs = null;
        Action<PressEnterEventArgs> action = args => { capturedArgs = args; };
        var cut = Render<AntDesign.Input<string>>(@<AntDesign.Input TValue="string" OnPressEnter="@(action)" />);
        
        //Act
        var input = cut.Find("input");
        await input.KeyPressAsync(new KeyboardEventArgs { Code = "Enter", CtrlKey = true });
        
        //Assert
        capturedArgs.Should().NotBeNull();
        capturedArgs!.Code.Should().Be("Enter");
        capturedArgs.CtrlKey.Should().BeTrue();

    }
}